package com.graden.background.common.conf.aop.operation;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.graden.background.common.conf.aop.sso.AopResourceUserInfoBean;
import com.graden.background.common.enums.OperationEnum;
import com.graden.background.common.utils.exception.ExceptionManager;
import com.graden.background.repository.sysuserlog.entity.SysUserLog;
import com.graden.background.service.sysuserlog.ISysUserLogService;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.Objects;

/**
 * <p>
 * 权限切面
 * </p>
 *
 * @author zjf
 * @since 2019-03-21
 */

@Aspect
@Component("OperationAop")
@Order(3)
@Slf4j
public class OperationAop {

	@Resource
	ApplicationContext applicationContext;

	@Resource
	ExceptionManager exceptionManager;

	@Resource
	ISysUserLogService iSysUserLogService;

	@Pointcut("@annotation(com.graden.background.common.conf.aop.operation.OperationAnnotation)")
	public void aPointcut() {
	}

	@After(value = "aPointcut()")
	public void afterAdvice(JoinPoint joinPoint) {

		//获取对方IP
		HttpServletRequest req = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
		String fromIP = req.getRemoteAddr();

		// 获取注解对象
		OperationAnnotation operationAnnotation = getAnnotation(joinPoint);

		//获取BipCode
		AopResourceUserInfoBean aopResourceUserInfoBean = applicationContext.getBean(AopResourceUserInfoBean.class);
		String bipCode = aopResourceUserInfoBean.getBipCode();


		if (Objects.nonNull(aopResourceUserInfoBean) && aopResourceUserInfoBean.getId() != null) {

			//管理员不拦截权限
			if (!aopResourceUserInfoBean.getIsAdmin()) {
				//拦截权限
				if (!isExistMenuPermission(aopResourceUserInfoBean, operationAnnotation)) {
					throw exceptionManager.create("PERMISSION_0001");
				}

				if (!isExistFunctionPermission(aopResourceUserInfoBean, operationAnnotation)) {
					throw exceptionManager.create("PERMISSION_0002");
				}
			}

			iSysUserLogService.save(getSysUserLog(joinPoint, bipCode, fromIP));
		}
	}

	private SysUserLog getSysUserLog(JoinPoint joinPoint, String bipCode, String fromIP) {
		// 获取注解对象
		OperationAnnotation operationAnnotation = getAnnotation(joinPoint);

		SysUserLog sysUserLog = new SysUserLog();
		sysUserLog.setId(0);
		sysUserLog.setBipCode(bipCode);
		sysUserLog.setFromIp(fromIP);
		sysUserLog.setParam(getParams(joinPoint));
		sysUserLog.setMenuId(operationAnnotation.operation().getLowestId());
		sysUserLog.setMenuName(operationAnnotation.operation().getLowestName());
		sysUserLog.setFunctionId(operationAnnotation.operation().getFunctionId());
		sysUserLog.setFunctionName(operationAnnotation.operation().getFunctionName());
		sysUserLog.setCreateTime(LocalDateTime.now());
		sysUserLog.setUpdateTime(LocalDateTime.now());
		sysUserLog.setStatus(true);

		return sysUserLog;
	}

	/**
	 * 是否有菜单权限
	 */
	private boolean isExistMenuPermission(AopResourceUserInfoBean aopResourceUserInfoBean, OperationAnnotation operationAnnotation) {

		//权限拦截忽略一些操作
		if (operationAnnotation.operation() == OperationEnum.LOGIN_COMMON
				|| operationAnnotation.operation() == OperationEnum.LOGIN_OUT
				|| operationAnnotation.operation() == OperationEnum.SYSTEM_USER_INFO
				|| operationAnnotation.operation() == OperationEnum.SYSTEM_USER_PASSWORD) {
			return true;
		}

		//判断菜单权限
		boolean isMenuPermission = false;
		if (Objects.nonNull(aopResourceUserInfoBean.getMenus())) {
			for (Integer menuId : aopResourceUserInfoBean.getMenus()) {
				if (operationAnnotation.operation().getMenuId() == menuId) {
					isMenuPermission = true;
					break;
				}
			}
		}

		return isMenuPermission;
	}

	/**
	 * 是否有功能权限
	 */
	private boolean isExistFunctionPermission(AopResourceUserInfoBean aopResourceUserInfoBean, OperationAnnotation operationAnnotation) {

		//权限拦截忽略一些操作
		if (operationAnnotation.operation() == OperationEnum.LOGIN_COMMON
				|| operationAnnotation.operation() == OperationEnum.LOGIN_OUT
				|| operationAnnotation.operation() == OperationEnum.SYSTEM_USER_SEE
				|| operationAnnotation.operation() == OperationEnum.SYSTEM_USER_INFO
				|| operationAnnotation.operation() == OperationEnum.SYSTEM_USER_PASSWORD
				|| operationAnnotation.operation() == OperationEnum.SYSTEM_OPERATION_SEE
				|| operationAnnotation.operation() == OperationEnum.SYSTEM_ROLE_SEE) {
			return true;
		}

		//判断功能权限
		boolean isFunctionPermission = false;
		if (Objects.nonNull(aopResourceUserInfoBean.getFunctions())) {
			for (Integer functionId : aopResourceUserInfoBean.getFunctions()) {
				if (operationAnnotation.operation().getFunctionId() == functionId) {
					isFunctionPermission = true;
					break;
				}
			}
		}

		return isFunctionPermission;
	}

	/**
	 * 是否存在注解，如果存在就获取
	 */
	private static OperationAnnotation getAnnotation(JoinPoint joinPoint) {
		Signature signature = joinPoint.getSignature();
		MethodSignature methodSignature = (MethodSignature) signature;
		Method method = methodSignature.getMethod();
		if (method != null) {
			return method.getAnnotation(OperationAnnotation.class);
		}
		return null;
	}

	/**
	 * 获取参数
	 */
	private String getParams(JoinPoint joinPoint) {
		JSONArray paramsJson = new JSONArray();
		Object[] params = joinPoint.getArgs();
		int i = 1;
		for (Object object : params) {
			JSONObject j = new JSONObject();
			j.put("param" + (i++), JSONObject.toJSON(object));
			paramsJson.add(j);
		}
		return paramsJson.isEmpty() ? null : paramsJson.toJSONString();
	}
}
